{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "17ad44da",
   "metadata": {},
   "source": [
    "### Python 3.8 - Assignment Expressions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40b0045a",
   "metadata": {},
   "source": [
    "Another enhancement to the Python core language that was introduced in Python 3.8 is **assignment expressions**."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cad52d44",
   "metadata": {},
   "source": [
    "You can see the pep for it here: [pep 572](https://peps.python.org/pep-0572/)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "285f5b11",
   "metadata": {},
   "source": [
    "Remember that an expression is simply a snippet of code that is evaluated.\n",
    "\n",
    "The following are all examples of expressions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "357659ec",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "1 + 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c7b05302",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 3, 4, 5, 6]"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[1, 2, 3] + [4, 5, 6]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a68603b6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'PYTHON'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"  python  \".lstrip().rstrip().upper()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "201b3156",
   "metadata": {},
   "source": [
    "And an assignment is simply when we assign an expression result to a variable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b08e7b15",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = 1 + 2\n",
    "b = [1, 2, 3] + [4, 5, 6]\n",
    "c = \"  python  \".lstrip().rstrip().upper()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "54232d7a",
   "metadata": {},
   "source": [
    "As you can see, we have to different steps here.\n",
    "\n",
    "We assign the result of an expression (the right hand side) to a variable (the left hand side) using the equals (`=`) sign."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0ca1920",
   "metadata": {},
   "source": [
    "So we have two **distinct** (totally separate) aspects here - the expression, and the assignment."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90b2d0ab",
   "metadata": {},
   "source": [
    "So what are **expression assignments**?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2065f05",
   "metadata": {},
   "source": [
    "Expression assignments allows us to assign expressions to a variable **inside** an expression, using the `:=` operator (the so-called *walrus* operator)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c400c75",
   "metadata": {},
   "source": [
    "Confusing? :-)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "084caec6",
   "metadata": {},
   "source": [
    "Let's take a look at a very simple example first:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "985d592b",
   "metadata": {},
   "source": [
    "Starting with an expression:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c9b56fb0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "1 + 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b89a8762",
   "metadata": {},
   "source": [
    "We could assign the result of that expression to some variable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "225ca5f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = 1 + 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1513437a",
   "metadata": {},
   "source": [
    "But, we could also write the expression and assignment this way (not the parentheses that enclose the expression):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "572aaf6a",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = (1 + 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d0e2885",
   "metadata": {},
   "source": [
    "With the expression assignment operator, we could actually assign the result of that expression inside the expression itself:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "0fa9c199",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(x := 1 + 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1eafbee8",
   "metadata": {},
   "source": [
    "As you can see, the expression returned a result (`3`), but it also assigned that result to the variable `x`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "915d5b48",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e35a8501",
   "metadata": {},
   "source": [
    "Note that the parentheses in this case are **necessary** - simply writing this would not work:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "890070de",
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (746607632.py, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  Input \u001b[0;32mIn [10]\u001b[0;36m\u001b[0m\n\u001b[0;31m    x := 1 + 2\u001b[0m\n\u001b[0m      ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "x := 1 + 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0b5f281",
   "metadata": {},
   "source": [
    "This is because the `:=` operator must be used inside an **expression**, so we can force it by using the parentheses."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3195a0a8",
   "metadata": {},
   "source": [
    "We could even do this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "6d1e232c",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = (x := 10 + 20)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35a7ed36",
   "metadata": {},
   "source": [
    "Then, `a`, and `x` are:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "96bf1532",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(30, 30)"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a, x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "638c195a",
   "metadata": {},
   "source": [
    "Yeah, even more confusing! But in a minute I'll show you why this can be very useful."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a625536",
   "metadata": {},
   "source": [
    "Before we move on to that, let's see how this assignment expression works when we deal with mutable objects such as lists:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "a2ee53ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "l1 = (l2 := [1, 2] + [3, 4])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e58d69f",
   "metadata": {},
   "source": [
    "Here, `l1` was the result of the concatenation of the two lists:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "2d3ae1a4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "([1, 2, 3, 4], 4393044992)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1, id(l1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88aee44a",
   "metadata": {},
   "source": [
    "But what about `l2`? It should be a list with the same values, but is it the same object reference as `l1`?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "fc8fe138",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "([1, 2, 3, 4], 4393044992)"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l2, id(l2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "243f0d28",
   "metadata": {},
   "source": [
    "And indeed, they are not only the same values, but the same object."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7268ec5",
   "metadata": {},
   "source": [
    "Usually this is not an issue, but keep it in mind because you end up with shared references that you may not realize exist."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e27c849e",
   "metadata": {},
   "source": [
    "So now, why is this useful?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a57d9679",
   "metadata": {},
   "source": [
    "Often, we end up writing expressions in terms of other sub expressions, not just for clarity, but sometimes to **avoid repeating** function calls or expression evaluations."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a686538",
   "metadata": {},
   "source": [
    "#### Example 1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3131d1f1",
   "metadata": {},
   "source": [
    "Suppose we have some long running function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "f66dd3b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "import math\n",
    "\n",
    "def slow_function(x, y):\n",
    "    time.sleep(0.5)\n",
    "    return round(math.sqrt(x**2 + y**2))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ddce261",
   "metadata": {},
   "source": [
    "Now executing this function will take about 2 seconds to run every time it is called, even when calling it with the same values (we could of course use some LRU caching, but only if the function is **deterministic** - if the function is reading data from a web site, or a database, the result for the same arguments may not be the same and so LRU caching is not even a viable option in this case).\n",
    "\n",
    "> A **deterministic** function is a function that for the **same** inputs, always returns the **same** result:\n",
    ">\n",
    ">  Obviously the function we have above is deterministic, but this one would not be:\n",
    ">```\n",
    ">def get_price(symbol):\n",
    ">    # query an API for latest price for symbol\n",
    ">    price = ...\n",
    ">    return price\n",
    ">``` \n",
    "> A function that returns the current date or time, or a random number, etc are all non-deterministic functions."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e503d9b",
   "metadata": {},
   "source": [
    "So, LRU caching is not always an option."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "05027bcc",
   "metadata": {},
   "source": [
    "Let's see an example of why we would want to assign the result of our long running function to a variable, instead of just using it directly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "a9babf8f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 4, 6, 8, 10]\n",
      "Elapsed: 7.6 seconds\n"
     ]
    }
   ],
   "source": [
    "from time import perf_counter\n",
    "\n",
    "start = perf_counter()\n",
    "even_results = []\n",
    "for i in range(10):\n",
    "    if slow_function(i, i) % 2 == 0:\n",
    "        even_results.append(slow_function(i, i))\n",
    "end = perf_counter()\n",
    "print(even_results)\n",
    "print(f'Elapsed: {end - start:.1f} seconds')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3bdd4e15",
   "metadata": {},
   "source": [
    "Well that was painfully slow!\n",
    "\n",
    "But notice that we are calling the same function, with the same arguments twice - we can eliminate that!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "19453896",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 4, 6, 8, 10]\n",
      "Elapsed: 5.0 seconds\n"
     ]
    }
   ],
   "source": [
    "start = perf_counter()\n",
    "even_results = []\n",
    "for i in range(10):\n",
    "    result = slow_function(i, i)\n",
    "    if result % 2 == 0:\n",
    "        even_results.append(result)\n",
    "end = perf_counter()\n",
    "print(even_results)\n",
    "print(f'Elapsed: {end - start:.1f} seconds')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b6091cb",
   "metadata": {},
   "source": [
    "So we are able to speed this code up, by using that interim `result` variable - also note how `result` is basically a throw away variable (we typically would not use such a variable outside the loop itself - exceptions happen of course)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8acf631f",
   "metadata": {},
   "source": [
    "But notice something about that code? It's ugly looking - we are building up a list by running through a loop and adding to an initially empty list, one element at a time."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6293f28c",
   "metadata": {},
   "source": [
    "We can do better! List comprehensions of course."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9cdff5a7",
   "metadata": {},
   "source": [
    "But... we can't write that `result = slow_function(i, i)` in our list comprehension - so we would be back to the original (and slower) may of doing it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "0d89b92d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 4, 6, 8, 10]\n",
      "Elapsed: 7.6 seconds\n"
     ]
    }
   ],
   "source": [
    "start = perf_counter()\n",
    "even_results = [\n",
    "    slow_function(i, i)\n",
    "    for i in range(10)\n",
    "    if slow_function(i, i) % 2 == 0\n",
    "]\n",
    "end = perf_counter()\n",
    "print(even_results)\n",
    "print(f'Elapsed: {end - start:.1f} seconds')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8db81b84",
   "metadata": {},
   "source": [
    ":-("
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c42d90a",
   "metadata": {},
   "source": [
    "And this is where the assignment expression operator comes in very handy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "eefb7ce7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 4, 6, 8, 10]\n",
      "Elapsed: 5.0 seconds\n"
     ]
    }
   ],
   "source": [
    "start = perf_counter()\n",
    "even_results = [\n",
    "    result\n",
    "    for i in range(10)\n",
    "    if (result := slow_function(i, i)) % 2 == 0\n",
    "]\n",
    "end = perf_counter()\n",
    "print(even_results)\n",
    "print(f'Elapsed: {end - start:.1f} seconds')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1c77b0c",
   "metadata": {},
   "source": [
    "Notice how using the `:=` operator, we assign the result of `slow_function(i, i)` to `result` as part of the expression itself, and then re-use that computed value for the elements of the list."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9e3744d",
   "metadata": {},
   "source": [
    "You may be asking yourself, why not write it this way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "db2bc22b",
   "metadata": {},
   "outputs": [],
   "source": [
    "del result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "cd112bb8",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'result' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Input \u001b[0;32mIn [22]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m even_results \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m      2\u001b[0m     (result \u001b[38;5;241m:=\u001b[39m slow_function(i, i))\n\u001b[1;32m      3\u001b[0m     \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;241m10\u001b[39m)\n\u001b[1;32m      4\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m result \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m      5\u001b[0m ]\n",
      "Input \u001b[0;32mIn [22]\u001b[0m, in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m      1\u001b[0m even_results \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m      2\u001b[0m     (result \u001b[38;5;241m:=\u001b[39m slow_function(i, i))\n\u001b[1;32m      3\u001b[0m     \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;241m10\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mresult\u001b[49m \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m      5\u001b[0m ]\n",
      "\u001b[0;31mNameError\u001b[0m: name 'result' is not defined"
     ]
    }
   ],
   "source": [
    "even_results = [\n",
    "    (result := slow_function(i, i))\n",
    "    for i in range(10)\n",
    "    if result % 2 == 0\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c0229a0",
   "metadata": {},
   "source": [
    "This happens because in a list comprehension, the loop starts running, then the `if` clause (if any) is evaluated, and then the element expression is evaluated - hence why we place the assignment expression in the `if`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc652ad3",
   "metadata": {},
   "source": [
    "**Example 2**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "093978ca",
   "metadata": {},
   "source": [
    "Here's another scenario where this new operator could be quite useful."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "edbdc1b7",
   "metadata": {},
   "source": [
    "You want to return the result of an expression but only if it satisfies some criteria.\n",
    "\n",
    "For example, let's say we write a generator function to produce n even random integers between 1 and 10:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "a75a9138",
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "random.seed(0)\n",
    "def even_random(n):\n",
    "    cnt = 0\n",
    "    while cnt <= n:\n",
    "        cnt += 1\n",
    "        number = random.randint(0, 10)\n",
    "        if number % 2 == 0:\n",
    "            yield number"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f19d723",
   "metadata": {},
   "source": [
    "We can then call the generator function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "53f19b5e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[6, 6, 0, 4, 8]"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(even_random(5))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "76ac9cf0",
   "metadata": {},
   "source": [
    "We can make our code a little more concise without losing readability by using the `:=` operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "b849d145",
   "metadata": {},
   "outputs": [],
   "source": [
    "random.seed(0)\n",
    "def even_random(n):\n",
    "    cnt = 0\n",
    "    while (cnt := cnt + 1) <= n:\n",
    "        if (number := random.randint(0, 10)) % 2 == 0:\n",
    "            yield number"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "b847739e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[6, 6, 0, 4, 8]"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(even_random(5))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5621f621",
   "metadata": {},
   "source": [
    "#### Example 3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e1719fd",
   "metadata": {},
   "source": [
    "Here's another example where we are consuming some generator, until some condition is met."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9822e4d5",
   "metadata": {},
   "source": [
    "Let's write a generator function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "623069a1",
   "metadata": {},
   "outputs": [],
   "source": [
    "def gen():\n",
    "    while True:\n",
    "        yield list(range(random.randint(0, 10)))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da617a93",
   "metadata": {},
   "source": [
    "And let's print out a frew values from this generator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "56b83447",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 1, 2]\n",
      "[0, 1, 2, 3, 4]\n",
      "[0, 1, 2, 3, 4, 5]\n",
      "[0, 1]\n",
      "[0, 1, 2]\n",
      "[]\n",
      "[0]\n",
      "[0, 1]\n",
      "[0, 1, 2]\n",
      "[0, 1, 2, 3, 4, 5, 6, 7]\n"
     ]
    }
   ],
   "source": [
    "random.seed(8)\n",
    "my_gen = gen()\n",
    "for _ in range(10):\n",
    "    print(next(my_gen))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad1bb10e",
   "metadata": {},
   "source": [
    "You'll notice that the fourth element is a list with 2 values.\n",
    "\n",
    "What we want to do now, is process the lists yielded by the generator, until we hit a list with two values, at which point we want to stop processing it."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25d4dab3",
   "metadata": {},
   "source": [
    "We could do it this way with a `while` loop:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "f5a068ab",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 1, 2]\n",
      "[0, 1, 2, 3, 4]\n",
      "[0, 1, 2, 3, 4, 5]\n"
     ]
    }
   ],
   "source": [
    "random.seed(8)\n",
    "my_gen = gen()\n",
    "while True:\n",
    "    l = next(my_gen)\n",
    "    if len(l) <= 2:\n",
    "        break\n",
    "    print(l)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fcf1f1a2",
   "metadata": {},
   "source": [
    "Instead, we could re-write this way using the `:=` operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "7568cf9c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 1, 2]\n",
      "[0, 1, 2, 3, 4]\n",
      "[0, 1, 2, 3, 4, 5]\n"
     ]
    }
   ],
   "source": [
    "random.seed(8)\n",
    "my_gen = gen()\n",
    "while len(l := next(my_gen)) > 2:\n",
    "    print(l)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d9f1b587",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
